//
//  SSOverlapDisplayGenerator.m
//  Investigation Catalyst
//
//  Created by Tobias Haughton on Mon Jan 12 2004.
//  Copyright (c) 2004 Starline Software Ltd. All rights reserved.
//

//SSOverlapDisplayGenerator creates a dictionary containing all of the objects needed to represent catalyst's
//overlap view.


#import "SSOverlapDisplayGenerator.h"


@implementation SSOverlapDisplayGenerator


- (NSMutableDictionary *)displayDictionaryForRect:(NSRect)rect rectStartDate:(NSCalendarDate *)fromDate unit:(float)unit pixelsPerUnit:(float)pixelsPerUnit textAttributes:(NSDictionary *)textAttributes
{
   NSMutableDictionary *displayDictionary = [[NSMutableDictionary alloc] init];
   NSMutableArray *unitDateStrings = [NSMutableArray array];
   NSMutableArray *secondaryUnitDateStrings = [NSMutableArray array];
   NSMutableArray *unitDateStringPositions = [NSMutableArray array];
   NSMutableArray *secondaryUnitDateStringPositions = [NSMutableArray array];
   float currentXPosition = NSMinX(rect);
   float maxX = NSMaxX(rect);
   float minY = NSMinY(rect);
   float maxY = NSMaxY(rect);
   NSTimeInterval timeIntervalsAfterFirstFullUnit;
   NSBezierPath *unitPath = [[[NSBezierPath alloc] init] autorelease];
   NSBezierPath *secondaryPath = [[[NSBezierPath alloc] init] autorelease];
   NSCalendarDate *date = fromDate;
   NSCalendarDate *lastFullUnitDate;
   SEL unitSelector = nil;
   int unitSelectorTrueValue;
   SEL secondaryUnitSelector;
   int secondaryUnitSelectorTrueValue;
   NSString *unitDescriptionString;
   NSString *secondaryUnitDescriptionString;
   int unitDisplayTestType = -1;

   [displayDictionary setObject:unitPath forKey:@"unitPath"];
   [displayDictionary setObject:secondaryPath forKey:@"secondaryPath"];
   [displayDictionary setObject:unitDateStrings forKey:@"unitDateStrings"];
   [displayDictionary setObject:secondaryUnitDateStrings forKey:@"secondaryUnitDateStrings"];
   [displayDictionary setObject:unitDateStringPositions forKey:@"unitDateStringPositions"];
   [displayDictionary setObject:secondaryUnitDateStringPositions forKey:@"secondaryUnitDateStringPositions"];
   [displayDictionary setObject:[NSValue valueWithRect:rect] forKey:@"rectValue"];
   [displayDictionary setObject:textAttributes forKey:@"textAttributes"];

   [unitPath setLineWidth:1];
   [secondaryPath setLineWidth:1];

   //need to move to back to the first full unit within rect
   NSNumber *unitNum = [NSNumber numberWithFloat:unit]; //for some reason millisecond and 100th second carn't be compared using == so we put the number in a nsnumber
   
   if([unitNum isEqualTo:[NSNumber numberWithFloat:SSMillisecondUnit]]) {
      lastFullUnitDate = [NSCalendarDate dateWithString:[NSString stringWithFormat:@"%@/%@/%@ %@/%@/%@/%@",
         [date descriptionWithCalendarFormat:@"%Y"], [date descriptionWithCalendarFormat:@"%m"], [date descriptionWithCalendarFormat:@"%d"],
         [date descriptionWithCalendarFormat:@"%H"], [date descriptionWithCalendarFormat:@"%M"], [date descriptionWithCalendarFormat:@"%S"],
         [date descriptionWithCalendarFormat:@"%F"]] calendarFormat:@"%Y/%m/%d %H/%M/%S/%F"];

      //get the number of time intervals that we need to move back
      timeIntervalsAfterFirstFullUnit = [date timeIntervalSinceDate:lastFullUnitDate];

      //change date so that it is at the start of the last full unit that we are using
      date = lastFullUnitDate;

      //change currentXPosition to reflect the change in date
      currentXPosition = (currentXPosition - ((timeIntervalsAfterFirstFullUnit / unit) * pixelsPerUnit));

      //limit the display based on how many pixels the user wants to display
      if(pixelsPerUnit < 1) {
         unitSelector = @selector(millisecondOfSecond);
         unitSelectorTrueValue = 0;
         unitDisplayTestType = 5;
      } else if(pixelsPerUnit < 3) {
         unitSelector = @selector(millisecondOfSecond);
         unitSelectorTrueValue = 0;
         unitDisplayTestType = 1;
      } else if(pixelsPerUnit < 6) {
         unitSelector = @selector(millisecondOfSecond);
         unitSelectorTrueValue = 0;
         unitDisplayTestType = 2;
      } else if(pixelsPerUnit < 11) {
         unitSelector = @selector(millisecondOfSecond);
         unitSelectorTrueValue = 0;
         unitDisplayTestType = 3;
      } else if(pixelsPerUnit < 20) {
         unitSelector = @selector(millisecondOfSecond);
         unitSelectorTrueValue = 0;
         unitDisplayTestType = 4;
      } //if it's above 20 don't include a test just display all units
      
      secondaryUnitSelector = @selector(millisecondOfSecond);
      secondaryUnitSelectorTrueValue = 0;
      unitDescriptionString = @"%F";
      secondaryUnitDescriptionString = @"%Y:%m:%d %H:%M:%S";
   } else if([unitNum isEqualTo:[NSNumber numberWithFloat:SS100THSecondUnit]]) {
      int milliseconds = [[date descriptionWithCalendarFormat:@"%F"] intValue];
      int millisecondsForNewDate = (milliseconds - (milliseconds % 10)); //remove the remainder
      lastFullUnitDate = [NSCalendarDate dateWithString:[NSString stringWithFormat:@"%@/%@/%@ %@/%@/%@/%d",
         [date descriptionWithCalendarFormat:@"%Y"], [date descriptionWithCalendarFormat:@"%m"], [date descriptionWithCalendarFormat:@"%d"],
         [date descriptionWithCalendarFormat:@"%H"], [date descriptionWithCalendarFormat:@"%M"], [date descriptionWithCalendarFormat:@"%S"],
         millisecondsForNewDate] calendarFormat:@"%Y/%m/%d %H/%M/%S/%F"];

      //get the number of time intervals that we need to move back
      timeIntervalsAfterFirstFullUnit = [date timeIntervalSinceDate:lastFullUnitDate];

      //change date so that it is at the start of the last full unit that we are using
      date = lastFullUnitDate;

      //change currentXPosition to reflect the change in date
      currentXPosition = (currentXPosition - ((timeIntervalsAfterFirstFullUnit / unit) * pixelsPerUnit));

      //limit the display based on how many pixels the user wants to display
      if(pixelsPerUnit < 1) {
         //less that 1 just display seconds
         unitSelector = @selector(hundredthOfSecondOfSecond);
         unitSelectorTrueValue = -1;
         unitDisplayTestType = 1;
      } else if(pixelsPerUnit < 3) {
         unitSelector = @selector(hundredthOfSecondOfSecond);
         unitSelectorTrueValue = 0;
         unitDisplayTestType = 1;
      } else if(pixelsPerUnit < 6) {
         unitSelector = @selector(hundredthOfSecondOfSecond);
         unitSelectorTrueValue = 0;
         unitDisplayTestType = 2;
      } else if(pixelsPerUnit < 11) {
         unitSelector = @selector(hundredthOfSecondOfSecond);
         unitSelectorTrueValue = 0;
         unitDisplayTestType = 3;
      } else if(pixelsPerUnit < 20) {
         unitSelector = @selector(hundredthOfSecondOfSecond);
         unitSelectorTrueValue = 0;
         unitDisplayTestType = 4;
      } //if it's above 20 don't include a test just display all units
      
      secondaryUnitSelector = @selector(millisecondOfSecond);
      secondaryUnitSelectorTrueValue = 0;
      unitDescriptionString = @"%F";
      secondaryUnitDescriptionString = @"%Y:%m:%d %H:%M:%S";
   } else if(unit == SSSecondUnit) {
      lastFullUnitDate = [NSCalendarDate dateWithYear:[date yearOfCommonEra]
                                                                month:[date monthOfYear]
                                                                  day:[date dayOfMonth]
                                                                 hour:[date hourOfDay]
                                                               minute:[date minuteOfHour]
                                                               second:[date secondOfMinute]
                                                             timeZone:nil];
      //get the number of time intervals that we need to move back
      timeIntervalsAfterFirstFullUnit = [date timeIntervalSinceDate:lastFullUnitDate];

      //change date so that it is at the start of the last full unit that we are using
      date = lastFullUnitDate;

      //change currentXPosition to reflect the change in date
      currentXPosition = (currentXPosition - ((timeIntervalsAfterFirstFullUnit / unit) * pixelsPerUnit));

      //limit the display based on how many pixels the user wants to display
      if(pixelsPerUnit < 1) {
         //less than 1 just display minutes
         unitSelector = @selector(secondOfMinute);
         unitSelectorTrueValue = -1;
         unitDisplayTestType = 1;
      } else if(pixelsPerUnit < 2) {
         unitSelector = @selector(secondOfMinute);
         unitSelectorTrueValue = 0;
         unitDisplayTestType = 1;
      } else if(pixelsPerUnit < 6) {
         unitSelector = @selector(secondOfMinute);
         unitSelectorTrueValue = 0;
         unitDisplayTestType = 2;
      } else if(pixelsPerUnit < 11) {
         unitSelector = @selector(secondOfMinute);
         unitSelectorTrueValue = 0;
         unitDisplayTestType = 3;
      } else if(pixelsPerUnit < 15) {
         unitSelector = @selector(secondOfMinute);
         unitSelectorTrueValue = 0;
         unitDisplayTestType = 4;
      } //if it's above 15 don't include a test just display all units
      
      secondaryUnitSelector = @selector(secondOfMinute);
      secondaryUnitSelectorTrueValue = 0;

      unitDescriptionString = @"%S";
      secondaryUnitDescriptionString = @"%Y:%m:%d %H:%M";
   } else if(unit == SSMinuteUnit) {
      lastFullUnitDate = [NSCalendarDate dateWithYear:[date yearOfCommonEra]
                                                                month:[date monthOfYear]
                                                                  day:[date dayOfMonth]
                                                                 hour:[date hourOfDay]
                                                               minute:[date minuteOfHour]
                                                               second:0
                                                             timeZone:nil];
      //get the number of time intervals that we need to move back
      timeIntervalsAfterFirstFullUnit = [date timeIntervalSinceDate:lastFullUnitDate];

      //change date so that it is at the start of the last full unit that we are using
      date = lastFullUnitDate;

      //change currentXPosition to reflect the change in date
      currentXPosition = (currentXPosition - ((timeIntervalsAfterFirstFullUnit / unit) * pixelsPerUnit));

      //limit the display based on how many pixels the user wants to display
      if(pixelsPerUnit < 1) {
         unitSelector = @selector(minuteOfHour);
         unitSelectorTrueValue = -1;
         unitDisplayTestType = 1;
      } else if(pixelsPerUnit < 2) {
         unitSelector = @selector(minuteOfHour);
         unitSelectorTrueValue = 0;
         unitDisplayTestType = 1;
      } else if(pixelsPerUnit < 6) {
         unitSelector = @selector(minuteOfHour);
         unitSelectorTrueValue = 0;
         unitDisplayTestType = 2;
      } else if(pixelsPerUnit < 11) {
         unitSelector = @selector(minuteOfHour);
         unitSelectorTrueValue = 0;
         unitDisplayTestType = 3;
      } else if(pixelsPerUnit < 15) {
         unitSelector = @selector(minuteOfHour);
         unitSelectorTrueValue = 0;
         unitDisplayTestType = 4;
      } //if it's above 15 don't include a test just display all units
      
      secondaryUnitSelector = @selector(minuteOfHour);
      secondaryUnitSelectorTrueValue = 0;
      unitDescriptionString = @"%M";
      secondaryUnitDescriptionString = @"%Y:%m:%d %H";
   } else if(unit == SSHourUnit) {
      lastFullUnitDate = [NSCalendarDate dateWithYear:[date yearOfCommonEra]
                                                                month:[date monthOfYear]
                                                                  day:[date dayOfMonth]
                                                                 hour:[date hourOfDay]
                                                               minute:0
                                                               second:0
                                                             timeZone:nil];
      //get the number of time intervals that we need to move back
      timeIntervalsAfterFirstFullUnit = [date timeIntervalSinceDate:lastFullUnitDate];

      //change date so that it is at the start of the last full unit that we are using
      date = lastFullUnitDate;

      //change currentXPosition to reflect the change in date
      currentXPosition = (currentXPosition - ((timeIntervalsAfterFirstFullUnit / unit) * pixelsPerUnit));

      //limit the display based on how many pixels the user wants to display
      if(pixelsPerUnit < 1) {
         unitSelector = @selector(hourOfDay);
         unitSelectorTrueValue = -1;
         unitDisplayTestType = 1;
      } else if(pixelsPerUnit < 2) {
         unitSelector = @selector(hourOfDay);
         unitSelectorTrueValue = 0;
         unitDisplayTestType = 1;
      } else if(pixelsPerUnit < 6) {
         unitSelector = @selector(hourOfDay);
         unitSelectorTrueValue = 0;
         unitDisplayTestType = 2;
      } else if(pixelsPerUnit < 11) {
         unitSelector = @selector(hourOfDay);
         unitSelectorTrueValue = 0;
         unitDisplayTestType = 3;
      } else if(pixelsPerUnit < 15) {
         unitSelector = @selector(hourOfDay);
         unitSelectorTrueValue = 0;
         unitDisplayTestType = 4;
      } //if it's above 15 don't include a test just display all units
      
      secondaryUnitSelector = @selector(hourOfDay);
      secondaryUnitSelectorTrueValue = 0;

      unitDescriptionString = @"%H";
      secondaryUnitDescriptionString = @"%Y:%m:%d";
   } else {
      //day unit
      lastFullUnitDate = [NSCalendarDate dateWithYear:[date yearOfCommonEra]
                                                                 month:[date monthOfYear]
                                                                   day:[date dayOfMonth]
                                                                  hour:0
                                                                minute:0
                                                                second:0
                                                              timeZone:nil];
      //get the number of time intervals that we need to move back
      timeIntervalsAfterFirstFullUnit = [date timeIntervalSinceDate:lastFullUnitDate];

      //change date so that it is at the start of the last full unit that we are using
      date = lastFullUnitDate;

      //change currentXPosition to reflect the change in date
      currentXPosition = (currentXPosition - ((timeIntervalsAfterFirstFullUnit / unit) * pixelsPerUnit));

      if(pixelsPerUnit < 0.5) {
         unitSelector = @selector(dayOfMonth);
         unitSelectorTrueValue = -1;
         secondaryUnitSelector = @selector(dayOfYear);
         secondaryUnitSelectorTrueValue = 1;
         unitDescriptionString = @"%m";
         secondaryUnitDescriptionString = @"%Y";
      } else if(pixelsPerUnit < 6) {
         unitSelector = @selector(dayOfMonth);
         unitSelectorTrueValue = 1;
         secondaryUnitSelector = @selector(dayOfYear);
         secondaryUnitSelectorTrueValue = 1;
         unitDescriptionString = @"%m";
         secondaryUnitDescriptionString = @"%Y";
      } else if(pixelsPerUnit < 11) {
         unitSelector = @selector(dayOfWeek);
         unitSelectorTrueValue = 1;
         secondaryUnitSelector = @selector(dayOfMonth);
         secondaryUnitSelectorTrueValue = 1;
         unitDescriptionString = @"%d";
         secondaryUnitDescriptionString = @"%Y:%m";
      } else {
         secondaryUnitSelector = @selector(dayOfMonth);
         secondaryUnitSelectorTrueValue = 1;
         unitDescriptionString = @"%d";
         secondaryUnitDescriptionString = @"%Y:%m";
      }

      unitDisplayTestType = 0;
   }

   while(currentXPosition < maxX) {
      //continue looping until we fill the rect
      NSString *unitString;
      NSString *secondaryUnitString;
      float unitStringWidth = [[date descriptionWithCalendarFormat:unitDescriptionString] sizeWithAttributes:textAttributes].width;

      if((int)[date performSelector:secondaryUnitSelector] == secondaryUnitSelectorTrueValue) {
         [secondaryPath moveToPoint:NSMakePoint(currentXPosition, minY)];
         [secondaryPath lineToPoint:NSMakePoint(currentXPosition, maxY)];
         //make a string out of the specified description
         secondaryUnitString = [date descriptionWithCalendarFormat:secondaryUnitDescriptionString];
         [secondaryUnitDateStrings addObject:secondaryUnitString];
         [secondaryUnitDateStringPositions addObject:[NSNumber numberWithFloat:(currentXPosition + 2.0)]];
      }

      if(unitSelector) {
         //unit selector specified, test if it's true
         BOOL shouldAddDateInformation = NO;
         switch(unitDisplayTestType) {
            case 0:
               if((int)[date performSelector:unitSelector] == unitSelectorTrueValue) {
                  //passed the test so set to YES
                  shouldAddDateInformation = YES;
               }
                 break;
            case 1:
               if(((int)[date performSelector:unitSelector] % 20) == unitSelectorTrueValue) {
                  //passed the test so set to YES
                  shouldAddDateInformation = YES;
               }
               break;
            case 2:
               if(((int)[date performSelector:unitSelector] % 10) == unitSelectorTrueValue) {
                  //passed the test so set to YES
                  shouldAddDateInformation = YES;
               }
                 break;
            case 3:
               if(((int)[date performSelector:unitSelector] % 5) == unitSelectorTrueValue) {
                  //passed the test so set to YES
                  shouldAddDateInformation = YES;
               }
                 break;
            case 4:
               if(((int)[date performSelector:unitSelector] % 2) == unitSelectorTrueValue) {
                  //passed the test so set to YES
                  shouldAddDateInformation = YES;
               }
               break;
            case 5:
               if(((int)[date performSelector:unitSelector] % 50) == unitSelectorTrueValue) {
                  //passed the test so set to YES
                  shouldAddDateInformation = YES;
               }
               break;
            default:
               break;
         }

         if(shouldAddDateInformation) {
            [unitPath moveToPoint:NSMakePoint(currentXPosition, minY)];
            [unitPath lineToPoint:NSMakePoint(currentXPosition, maxY)];

            unitString = [date descriptionWithCalendarFormat:unitDescriptionString];

            if([unitString intValue] != 0) {
               //make a string out of the specified description
               if(unit == SSDayUnit) {
                  [unitDateStrings addObject:unitString];
                  [unitDateStringPositions addObject:[NSNumber numberWithFloat:(currentXPosition + 2.0)]];
               } else {
                  //move over line
                  [unitDateStrings addObject:unitString];
                  [unitDateStringPositions addObject:[NSNumber numberWithFloat:(currentXPosition - (unitStringWidth / 2))]];
               }
            }
         }
      } else {
         //no selector specified create the data
         [unitPath moveToPoint:NSMakePoint(currentXPosition, minY)];
         [unitPath lineToPoint:NSMakePoint(currentXPosition, maxY)];

         unitString = [date descriptionWithCalendarFormat:unitDescriptionString];

         if([unitString intValue] != 0) {
            //make a string out of the specified description
            if(unit == SSDayUnit) {
               [unitDateStrings addObject:unitString];
               [unitDateStringPositions addObject:[NSNumber numberWithFloat:(currentXPosition + 2.0)]];
            } else {
               //move over line
               [unitDateStrings addObject:unitString];
               [unitDateStringPositions addObject:[NSNumber numberWithFloat:(currentXPosition - (unitStringWidth / 2))]];
            }
         }
      }
      
      currentXPosition += pixelsPerUnit;

      date = [date addTimeInterval:unit]; //add unit to the date
   }

   [displayDictionary autorelease];
   return displayDictionary;
}


@end
